#include "sys.h"
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "lv_drivers/indev/evdev.h"
#include "lv_drivers/display/sunxifb.h"
#include "sound.h"

#define FONT_DIR "./res/fonts/"

static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br);
static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p);


/*
 *@brief 初始化一些必要资源
 */
void sys_init(void)
{
    snd_pcm_init();
    snd_mixer_init();
}

/*
 *@brief 为lvgl提供心跳tick
 */
uint32_t sys_tick(void)
{
    static uint64_t start_ms = 0;
    if(start_ms == 0) 
    {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    uint64_t now_ms;
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

    uint32_t time_ms = now_ms - start_ms;
    return time_ms;
}

/*
 *@brief 初始化显示设备
 */
void lv_port_disp_init(void)
{
    uint32_t rotated = LV_DISP_ROT_NONE;

    /*Linux frame buffer device init*/
    sunxifb_init(rotated);

    /*A buffer for LittlevGL to draw the screen's content*/
    static uint32_t width, height;
    sunxifb_get_sizes(&width, &height);

    static lv_color_t *buf;
    buf = (lv_color_t*) malloc(width * height * sizeof (lv_color_t));

    if (buf == NULL) {
        sunxifb_exit();
        printf("malloc draw buffer fail\n");
        exit(-1);
    }

    /*Initialize a descriptor for the buffer*/
    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf, NULL, width * height);

    /*Initialize and register a display driver*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf   = &disp_buf;
    disp_drv.flush_cb   = sunxifb_flush;
    disp_drv.hor_res    = width;
    disp_drv.ver_res    = height;
    disp_drv.rotated    = rotated;
    lv_disp_drv_register(&disp_drv);
}

/*
 *@brief 初始化输入设备
 */
void lv_port_indev_init(void)
{
    evdev_init();
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);                /*Basic initialization*/
    indev_drv.type =LV_INDEV_TYPE_POINTER;        /*See below.*/
    indev_drv.read_cb = evdev_read;               /*See below.*/
    /*Register the driver in LVGL and save the created input device object*/
    lv_indev_t * evdev_indev = lv_indev_drv_register(&indev_drv);
}

/*
 *@brief 初始化文件系统
 */
void lv_port_fs_init(void)
{
    static lv_fs_drv_t fs_drv;

    lv_fs_drv_init(&fs_drv);

    fs_drv.letter = 'S';
    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;

    lv_fs_drv_register(&fs_drv);
}

static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode)
{
    char smode[4] = {0};
    FILE *fp = NULL;

    if (mode == LV_FS_MODE_WR)
        strcpy(smode, "wb");
    else if (mode == LV_FS_MODE_RD)
        strcpy(smode, "rb");
    else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
        strcpy(smode, "wb+");
    else
        return NULL;

    fp = fopen(path, smode);

    return fp;
}

static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    FILE *fp = (FILE *)file_p;

    if (0 == fclose(fp))
        res = LV_FS_RES_OK;
    else
        res = LV_FS_RES_FS_ERR;

    return res;
}

static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    FILE *fp = (FILE *)file_p;

    *br = fread(buf, (size_t)1, (size_t)btr, fp);
    if (*br == btr)
        res = LV_FS_RES_OK;
    else
        res = LV_FS_RES_FS_ERR;

    return res;
}

static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    FILE *fp = (FILE *)file_p;

    if (0 == fseek(fp, pos, whence))
        res = LV_FS_RES_OK;
    else
        res = LV_FS_RES_FS_ERR;

    return res;
}

static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p)
{
    (void)drv;
    FILE *fp = (FILE *)file_p;

    *pos_p = ftell(fp);

    return LV_FS_RES_OK;
}

void sys_exit(void)
{
    sunxifb_exit();

    snd_pcm_dev_close();
    snd_mixer_dev_close();
}